package com.haoqizhe.kernel.shiro.jwt;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.MD5;
import com.haoqizhe.kernel.jwt.utils.JwtTokenUtil;
import com.haoqizhe.kernel.redis.common.util.RedisUtil;
import com.haoqizhe.kernel.shiro.properties.ShiroProperties;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;

import java.util.Arrays;
import java.util.List;

/**
 * jwt redis操作
 * redis 保证单点登陆
 *
 * @author haoqizhe.li
 * @date 2021/01/26 23:39
 */
@Slf4j
public class JwtRedis {

    private RedisUtil redisUtil;

    private ShiroProperties shiroProperties;

    private JwtTokenUtil jwtTokenUtil;

    public JwtRedis(RedisUtil redisUtil, ShiroProperties shiroProperties, JwtTokenUtil jwtTokenUtil) {
        this.redisUtil = redisUtil;
        this.shiroProperties = shiroProperties;
        this.jwtTokenUtil = jwtTokenUtil;
    }

    /**
     * 获取单点登陆token key
     * 支持多端
     *
     * @param token
     * @return
     */
    public String tokenKey(String token) {
        String tokenMd5 = MD5.create().digestHex(token);
        Claims claims = jwtTokenUtil.getClaimsFromToken(token);
        Object device = claims.get(JwtToken.DEVICE);

        String tokenPrefix = shiroProperties.getRedis().getTokenPrefix();
        tokenPrefix = StrUtil.endWith(tokenPrefix, ":") ? tokenPrefix + tokenMd5 : tokenPrefix + ":" + tokenMd5;
        return device == null ? tokenPrefix : tokenPrefix + ":" + String.valueOf(device);
    }

    /**
     * 获取单点登陆 user key
     * 支持多端
     *
     * @param username
     * @param token
     * @return
     */
    public String userKey(String username, String token) {
        Claims claims = jwtTokenUtil.getClaimsFromToken(token);
        Object device = claims.get(JwtToken.DEVICE);
        String userPrefix = shiroProperties.getRedis().getUserPrefix();
        userPrefix = StrUtil.endWith(userPrefix, ":") ? userPrefix + username : userPrefix + ":" + username;
        return device == null ? userPrefix : userPrefix + ":" + String.valueOf(device);
    }

    public String roleKey(String username) {
        String rolePrefix = shiroProperties.getRedis().getRolePrefix();
        return StrUtil.contains(rolePrefix, "%s") ? String.format(rolePrefix, username) :
                StrUtil.endWith(rolePrefix, ":") ? rolePrefix + username : rolePrefix + ":" + username;
    }

    public String permissionKey(String username) {
        String permissionsPrefix = shiroProperties.getRedis().getPermissionsPrefix();
        return StrUtil.contains(permissionsPrefix, "%s") ? String.format(permissionsPrefix, username) :
                StrUtil.endWith(permissionsPrefix, ":") ? permissionsPrefix + username : permissionsPrefix + ":" + username;
    }

    public boolean existToken(String token) {
        return redisUtil.exists(tokenKey(token));
    }

    public boolean setToken(String token,String username) {
        return redisUtil.set(tokenKey(token), username);
    }

    public boolean setUser(String username, String token) {

        String tokenMd5 = MD5.create().digestHex(token);
        Claims claims = jwtTokenUtil.getClaimsFromToken(token);
        Object device = claims.get(JwtToken.DEVICE);

        String userKey = userKey(username, token);
        Object obj = redisUtil.get(userKey);

        if (ObjectUtil.isNotNull(obj) && !tokenMd5.equals(obj.toString())) {
            String tokenPrefix = shiroProperties.getRedis().getTokenPrefix();
            String oldMd5 = obj.toString();
            String tokenKey = StrUtil.endWith(tokenPrefix, ":") ? tokenPrefix + oldMd5 : tokenPrefix + ":" + oldMd5;
            tokenKey = device == null ? tokenKey : tokenKey + ":" + String.valueOf(device);

            log.debug("delete key {} | {}", tokenKey, obj);
            redisUtil.remove(tokenKey);
        }

        return redisUtil.set(userKey(username, token), tokenMd5);
    }

    public void setRoles(String username, List<String> roles) {
        if (CollUtil.isEmpty(roles)) {
            return;
        }
        redisUtil.set(roleKey(username), String.join(",", roles));
    }


    public List<String> getRoles(String username) {
        String key = roleKey(username);
        if (!redisUtil.exists(key)) {
            return null;
        }
        return Arrays.asList(String.valueOf(redisUtil.get(key)));
    }

    public void setPermissions(String username, List<String> permissios) {
        if (CollUtil.isEmpty(permissios)) {
            return;
        }
        redisUtil.set(permissionKey(username), String.join(",", permissios));
    }

    public List<String> getPermissions(String username) {
        String key = roleKey(username);
        if (!redisUtil.exists(key)) {
            return null;
        }
        return Arrays.asList(String.valueOf(redisUtil.get(key)));
    }


}
